package com.vision.sales.product.cl.ui;

import static com.vision.sales.cl.SalesServices.productSvc;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.core.client.GWT;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.SimpleBeanEditorDriver;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.Style.SelectionMode;
import com.sencha.gxt.data.client.loader.RpcProxy;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.loader.LoadResultListStoreBinding;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoader;
import com.sencha.gxt.widget.core.client.TabItemConfig;
import com.sencha.gxt.widget.core.client.Window;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.event.HideEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.form.TextArea;
import com.sencha.gxt.widget.core.client.form.TextField;
import com.sencha.gxt.widget.core.client.form.validator.MaxLengthValidator;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.GridView;
import com.sencha.gxt.widget.core.client.toolbar.PagingToolBar;
import com.vision.core.cl.BaseModule;
import com.vision.core.cl.Callback;
import com.vision.core.cl.event.CreatedEvent;
import com.vision.core.cl.event.CreatedEvent.CreationHandler;
import com.vision.core.cl.ui.ConfirmDialogHandler;
import com.vision.core.cl.ui.TabItemPanel;
import com.vision.core.cl.ui.UI;
import com.vision.core.cl.ui.component.BasicGridView;
import com.vision.sales.cl.SalesServices;
import com.vision.sales.cl.SalesTasks;
import com.vision.sales.cl.data.SalesInvoiceProperties;
import com.vision.sales.cl.event.ProductCreatedEvent;
import com.vision.sales.cl.resource.SalesResources;
import com.vision.sales.cl.ui.component.ProductComboBox;
import com.vision.sales.cl.ui.component.TransactionLoadConfig;
import com.vision.sales.cl.ui.component.TransactionNoCell;
import com.vision.sales.cm.db.data.Product;
import com.vision.sales.cm.db.data.Product.FieldSpecs;
import com.vision.sales.cm.db.data.SalesInvoice;

/**
 * 
 * @author Mark
 * 
 */
public class ProductsPanel extends TabItemPanel implements Editor<Product> {

	interface Binder extends UiBinder<Widget, ProductsPanel> { }

	private static final Binder binder = GWT.create(Binder.class);
	
	@UiField VerticalLayoutContainer mainPanel;
	
	// Ignored by the form driver since these are not fields in the Product
	@Ignore @UiField TextButton refreshBtn;
	@Ignore @UiField TextButton addBtn;
	@Ignore @UiField TextButton deleteBtn;
	
	@Ignore @UiField ProductComboBox productComboBox;
	
	// Form fields that are bound by the form driver to the fields in the Product class.
	// The name of these form fields must match the name of the field in the Product class.
	@UiField TextField code;
	@UiField TextField name;
	@UiField TextField group;
	@UiField TextArea description;
	
	// Ignored by the form driver since these are not fields in the Product
	@Ignore @UiField TextButton saveBtn;
	@Ignore @UiField TextButton cancelBtn;
	
	// Interface used to bind the form fields to the Product fields
	interface ProductDriver extends SimpleBeanEditorDriver<Product, ProductsPanel> { }
	
	// Instance of the ProductDriver interface
	private ProductDriver driver = GWT.create(ProductDriver.class);
	
	@UiField(provided = true) Grid<SalesInvoice> salesGrid;
	@UiField PagingToolBar salesGridPagingToolbar;
	PagingLoader<PagingLoadConfig, PagingLoadResult<SalesInvoice>> salesGridLoader;
	
	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	public ProductsPanel() {
		// Global Handlers -- should be registered only once
		BaseModule.addHandler(ProductCreatedEvent.TYPE, new CreationHandler<Product>() {
			@Override
			public void onCreated(CreatedEvent<Product> e) {
				productComboBox.getStore().add(e.getObject());
			}
		});
		
		salesGrid = initializeSalesGrid();
		
		setWidget(binder.createAndBindUi(this));
		
		// Initialize the driver, used to run the form editor
		driver.initialize(this);
		
		code.addValidator(new MaxLengthValidator(FieldSpecs.CODE_MAX));
		name.addValidator(new MaxLengthValidator(FieldSpecs.NAME_MAX));
		name.addValidator(new MaxLengthValidator(FieldSpecs.GROUP_CODE_MAX));
		description.addValidator(new MaxLengthValidator(FieldSpecs.DESC_MAX));
		
		{
			// Initialize Loader for sales grid
			RpcProxy<PagingLoadConfig, PagingLoadResult<SalesInvoice>> proxy = new RpcProxy<PagingLoadConfig, PagingLoadResult<SalesInvoice>>() {
			    @Override
			    public void load(PagingLoadConfig loadConfig, AsyncCallback<PagingLoadResult<SalesInvoice>> callback) {
			    	Product product = productComboBox.getCurrentValue();
			    	if (product == null) return;
			    	TransactionLoadConfig tConfig = new TransactionLoadConfig(loadConfig);
			    	tConfig.setProducts(Arrays.asList(new Product[]{ product }));
			    	SalesServices.salesInvoiceSvc().getList(tConfig, callback);
			    }
		    };
		    
		    salesGridLoader = new PagingLoader<PagingLoadConfig, PagingLoadResult<SalesInvoice>>(proxy);
		    salesGridLoader.addLoadHandler(new LoadResultListStoreBinding<PagingLoadConfig, SalesInvoice, PagingLoadResult<SalesInvoice>>(salesGrid.getStore()));
	        
	        salesGrid.setLoader(salesGridLoader);
	        salesGrid.setLoadMask(true);
			
			salesGridPagingToolbar.bind(salesGridLoader);
		}
	}
	
	// UiFactory Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	/**
	 * Used to create the 'grid' field in the XML file. 
	 * 
	 * @return
	 */
	/*
	@UiFactory
	Grid<Product> initializeGrid() {
		ProductProperties props = GWT.create(ProductProperties.class);

		List<ColumnConfig<Product, ?>> columns = new ArrayList<ColumnConfig<Product, ?>>();

		columns.add(UI.column(props.code(), UIConstants.CODE_COL_WIDTH, "Code"));
		columns.add(UI.column(props.name(), UIConstants.NAME_COL_WIDTH, "Name"));
		ColumnConfig<Product, String> descCol = UI.column(props.description(), UIConstants.NAME_COL_WIDTH, "Description");
		columns.add(descCol);
		
		ColumnModel<Product> cm = new ColumnModel<Product>(columns);

		final Grid<Product> grid = new Grid<Product>(new ListStore<Product>(props.key()), cm);
		grid.setAllowTextSelection(false);
		grid.setHeight(300);
		grid.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
		
		GridView<Product> view = new BasicGridView<Product>();
		view.setEmptyText("There are no products to display.");
		view.setAutoExpandColumn(descCol);
		grid.setView(view);
		
		// Add a listener to the grid
		grid.getSelectionModel().addSelectionChangedHandler(new SelectionChangedHandler<Product>() {
			@Override
			public void onSelectionChanged(SelectionChangedEvent<Product> event) {
				final Product product = grid.getSelectionModel().getSelectedItem();
				if (product == null)
					return;
				
				if (driver.isDirty()) { // If there are unsaved changes in the form
					UI.confirm("Unsaved Changes", "There are unsaved changes. Would you like to continue loading?",
						new ConfirmDialogHandler() {
							@Override
							public void onYes(HideEvent event) {
								edit(product);
							}
						}
					);
				} else {
					edit(product);
				}
			}
		});
		
		return grid;
	}
	*/
	
	Grid<SalesInvoice> initializeSalesGrid() {
		SalesInvoiceProperties props = SalesInvoiceProperties.INSTANCE;
		ListStore<SalesInvoice> store = new ListStore<SalesInvoice>(props.key());
		
		List<ColumnConfig<SalesInvoice, ?>> columns = new ArrayList<ColumnConfig<SalesInvoice, ?>>();

		columns.add(UI.column(props.date(), 40, "Date", UI.dateCell()));
		columns.add(UI.column(props.groupAndNo(), 45, "Invoice No.", new TransactionNoCell<SalesInvoice>(store)));
		columns.add(UI.column(props.customerCode(), 70, "Customer"));
		columns.add(UI.column(props.poNo(), 60, "PO No."));
		columns.add(UI.column(props.grossSales(), 55, "Gross Sales", UI.decimalCell(), HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.balance(), 55, "Balance", UI.decimalCell(), HasHorizontalAlignment.ALIGN_RIGHT));
		
		ColumnModel<SalesInvoice> cm = new ColumnModel<SalesInvoice>(columns);

		Grid<SalesInvoice> grid = new Grid<SalesInvoice>(store, cm);
		grid.setAllowTextSelection(false);
		grid.getSelectionModel().setSelectionMode(SelectionMode.MULTI);
		grid.setHeight(300);
		
		GridView<SalesInvoice> view = new BasicGridView<SalesInvoice>();
		view.setAutoExpandColumn(columns.get(2));
		view.setEmptyText("There are no sales invoices to display.");
		grid.setView(view);
		
		return grid;
	}
	
	// Logic %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	/**
	 * Loads <tt>product</tt> into the form editor.
	 * 
	 * @param product
	 */
	private void edit(final Product product) {
		driver.edit(product);

		refreshUIControls(product != null);

		if (product == null)
			return;
		
		salesGridLoader.load();
	}
	
	private void refreshUIControls(boolean enabled) {
		deleteBtn.setEnabled(enabled);
		
		saveBtn.setEnabled(enabled);
		cancelBtn.setEnabled(enabled);

		name.setEnabled(enabled);
		group.setEnabled(enabled);
		description.setEnabled(enabled);
	}
	
	// Listeners %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

	@UiHandler("productComboBox")
	void customerComboBoxOnSelection(SelectionEvent<Product> e) {
		Product selectedProduct = e.getSelectedItem();
		edit(selectedProduct);
	}
	
	// Called when the 'Refresh' button is clicked
	@UiHandler("refreshBtn")
	void refreshClicked(SelectEvent e) {
		loadData();
	}
	
	// Called when the 'Add' button is clicked
	@UiHandler("addBtn")
	void addClicked(SelectEvent e) {
		Window w = (Window) new CreateProductWindow().asWidget();
		w.show();
	}
	
	// Called when the 'Delete' button is clicked
	@UiHandler("deleteBtn")
	void deleteClicked(SelectEvent e) {
		final Product selected = productComboBox.getCurrentValue(); 
		if (selected == null)
			return;
		
		UI.confirm("Confirm Delete", "Are you sure you want to delete '" + selected.getCode() + "'?",
			new ConfirmDialogHandler() {
				@Override
				public void onYes(HideEvent event) {
					UI.mask(mainPanel, "Deleting " + selected.getCode() + "...");
					productSvc().delete(selected, new Callback<Product>(mainPanel) {
						@Override
						protected void handleSuccess(Product result) {
							productComboBox.getStore().remove(result);
							
							// Clear the form editor if necessary
							Product inEditor = driver.flush();
							if (inEditor != null && inEditor.getCode().equals(result.getCode()))
								edit(null);
						}
					});
				}
			}
		);
	}
	
	// Called when the 'Save' button is clicked
	@UiHandler("saveBtn")
	void saveClicked(SelectEvent e) {
		Product product = driver.flush();
		if (driver.hasErrors())
			return;
		
		UI.mask(mainPanel, "Updating product...");
		productSvc().update(product, new Callback<Product>(mainPanel) {
			@Override
			protected void handleSuccess(Product result) {
				productComboBox.getStore().update(result);
				edit(result);
			}
		});
	}
	
	// Called when the 'Cancel' button is clicked
	@UiHandler("cancelBtn")
	void cancelClicked(SelectEvent e) {
		edit(null);
	}
	 
	// ITabItemPanel Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	@Override
	public String getPanelId() {
		return SalesTasks.PRODUCTS.getId();
	}
	
	@Override
	public TabItemConfig getConfig() {
		if (config == null) {
			config = new TabItemConfig("Products", true);
			config.setIcon(SalesResources.INSTANCE.products());
		}
		return config;
	}
	
	@Override
	public void loadData() {
		// Load data
		Log.debug("Loading products...");
		
		UI.mask(mainPanel, "Loading products...");
		productSvc().getAll(new Callback<List<Product>>(mainPanel) {
			@Override
			protected void handleSuccess(List<Product> result) {
				productComboBox.getStore().clear();
				productComboBox.getStore().addAll(result);

				edit(null); // Clear the form
			}
		});
	}
	
}
